<?php

class Database {

    // đối tượng kết nối
    public $_db;

    public function __construct() {
        return $this->openConnection();
    }

    // hàm mở kết nối database
    // hàm trả về đối tượng kết nối
    function openConnection() {
        try {     
            $host = HOST;
            $user = DATABASE_USER;
            $pass = DATABASE_PASS;
            $database = DATABASE_NAME;
            // khởi tạo đối tượng PDO
            // PDO::MYSQL_ATTR_INIT_COMMAND =>  "SET NAMES 'UTF8'" cho phép hiển thị các ký tự unicode (tiếng việt)

            $this->_db = new PDO('mysql:host=' . $host . ';dbname=' . $database, $user, $pass, array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'UTF8'"));
            //$db = new PDO('mysql:host='.$host.';dbname='.$database, $user, $pass);
            // thiết lập chế độ thông báo lỗi củ PDO
            // ERRMODE_EXCEPTION: khi gặp lỗi nó sẽ đưa ra exception và cho chúng ta xử lý code sẽ dừng
            // ERRMODE_SILENT: Khi gặp lỗi thì nó sẽ bỏ qua và tiếp tục chạy. Cái này tiện cho production
            // ERRMODE_WARNING: Khi gặp lỗi nó sẽ xuất ra thông báo và tiếp tục chạy. Cái này tiện cho việc debug
            $this->_db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            //$this->_db->setAttribute(PDO::ATTR_AUTOCOMMIT, false);
            // bắt đầu mở giao dịch
            // chế độ autocommit đã bị tắt
            $this->_db->beginTransaction();


            // trả về đối tượng kết nối
            return $this->_db;
        }
        catch (PDOException $ex) {
            echo $ex->getMessage();
            exit();
        }
    }

    // hoàn tất transaction
    public function submitChanges() {
        if ($this->_db != null) {
            $result = false;

            // hoàn tất giao dịch
            // hàm commit trả về true nếu thành công, ngược lại là false
            $result = $this->_db->commit();

            // sau khi hoàn thành giao dịch thì ngắt kết nối
            $this->_db = null;


            return $result;
        }
    }

    // quay trở lại các giao dịch
    function rollback() {
        if ($this->_db != null) {
            $this->_db->rollback();
        }
    }

    //khong duoc xai
    function lastInsertID() {
        if ($this->_db != null) {
            return $this->_db->lastInsertID();
        }
    }

    // hàm thực thi các câu lệnh sql cho việc Insert, Delete, Update
    // với 2 tham số trở lên 
    
    function executeNonQuery($sqlQuery, $parameters = array(), $values = array(), $types = array(), $getLastInsertID = false) {
        if ($this->_db != null) {

            $statement = $this->_db->prepare($sqlQuery);

            // nếu các tham số truyền vào không rỗng thì tiến hành bind dữ liệu
            if ($parameters != null and $values != null and $types != null) {
                for ($i = 0; $i < count($parameters); $i++) {
                    // bind dữ liệu
                    $statement->bindValue($parameters[$i], $values[$i], $types[$i]);
                }
            }

//            echo $statement->queryString;
            // câu lệnh trả về số dòng thực thi thành công    
            $affected = $statement->execute();


            // nếu thực thi thành công
            if ($affected > 0) {
                // nếu có lấy giá trị id cuối cùng
                if ($getLastInsertID == true) {
                    // trả về giá trị id mới thêm
                    return $this->_db->lastInsertId();
                }
                else {
                    return $affected;
                }
            }
            else {
                return 0;
            }
        }
    }

    // hàm thực thi các câu lệnh sql cho việc Select
    // với 2 tham số trở lên
    function executeQuery($sqlQuery, $parameters = array(), $values = array(), $types = array()) {
        if ($this->_db != null) {
            // khởi tạo đối tượng PDOStatement (truyền cầu query vào đối tượng)    
            $statement = $this->_db->prepare($sqlQuery);

            // nếu các tham số truyền vào không rỗng thì tiến hành bind dữ liệu
            if ($parameters != null and $values != null and $types != null) {
                for ($i = 0; $i < count($parameters); $i++) {
                    // bind dữ liệu
                    $statement->bindValue($parameters[$i], $values[$i], $types[$i]);
                }
            }


            // thực thi câu lệnh
            $statement->execute();

            // nếu số dòng lấy được là lớn hơn 0
            if ($statement->rowCount() > 0) {
                // thiết lập chế độ fetch dữ liệu
                // PDO::FETCH_BOTH (default): Trả về một mảng bao gồm cả thứ tự
                // PDO::FETCH_ASSOC: Trả về một mảng
                // PDO::FETCH_OBJ: Trả về một object
                $statement->setFetchMode(PDO::FETCH_OBJ);

                // trã về đối tượng Statement
                return $statement;
            }
            else {
                return null;
            }
        }
    }
     // hàm thực thi các câu lệnh sql cho việc Select
    // với 2 tham số trở lên
    function executeQueryXtemplate($sqlQuery, $parameters = array(), $values = array(), $types = array()) {
        if ($this->_db != null) {
            // khởi tạo đối tượng PDOStatement (truyền cầu query vào đối tượng)    
            $statement = $this->_db->prepare($sqlQuery);

            // nếu các tham số truyền vào không rỗng thì tiến hành bind dữ liệu
            if ($parameters != null and $values != null and $types != null) {
                for ($i = 0; $i < count($parameters); $i++) {
                    // bind dữ liệu
                    $statement->bindValue($parameters[$i], $values[$i], $types[$i]);
                }
            }


            // thực thi câu lệnh
            $statement->execute();

            // nếu số dòng lấy được là lớn hơn 0
            if ($statement->rowCount() > 0) {
                // thiết lập chế độ fetch dữ liệu
                // PDO::FETCH_BOTH (default): Trả về một mảng bao gồm cả thứ tự
                // PDO::FETCH_ASSOC: Trả về một mảng
                // PDO::FETCH_OBJ: Trả về một object
                $statement->setFetchMode(PDO::FETCH_ASSOC);

                // trã về đối tượng Statement
                return $statement;
            }
            else {
                return null;
            }
        }
    }

}

?>